package com.exoweb.games.hexagone.view
{
    import flash.display.*;
    import flash.events.*;

    import com.exoweb.games.hexagone.view.Cell;

    public class CellMap extends Sprite
    {
        public static const ROW:int = 11;
        public static const COL:int = 11;
        public static const ERASABLE_LIMIT:int = 4;

        public static const STATE_NORMAL:int = 0;
        public static const STATE_SWAPPING:int = 1;
        public static const STATE_ERASING:int = 2;
        public static const STATE_DROPPING:int = 3;

        public static const DIRECTION:int = 6;
        public static const DX:Array = [[0, -1, 0, 1, 1, 1],
                                        [-1, -1, -1, 0, 1, 0]];
        public static const DY:Array = [[-1, 0, 1, 1, 0, -1],
                                        [-1, 0, 1, 1, 0, -1]];

        private var map:Array;
        private var state:int;
        private var selectCell:Cell;
        private var overCell:Cell;
        private var movingCellAmount:int;
        private var erasingCellAmount:int;
        private var erasedCellAmount:int;

        public function CellMap()
        {
            map = new Array();
            for(var row:int=0; row<ROW; row++)
            {
                var l:int = COL;
                if((row & 1) == 1)
                {
                    l++;
                }

                var array:Array = new Array();
                for(var col:int=0; col<l; col++)
                {
                    var cell:Cell = new Cell(Cell.TYPE_EMPTY, col, row);
                    cell.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
                    cell.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
                    cell.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
                    cell.addEventListener(Cell.CELL_MOVED, cellMoved);
                    cell.addEventListener(Cell.CELL_ERASED, cellErased);
                    cell.display();
                    array.push(cell);
                    addChild(cell);
                }
                map.push(array);
            }
            mouseChildren = false;
            mouseEnabled = false;
        }

        public function start():void
        {
            state = STATE_NORMAL;
            movingCellAmount = 0;
            erasingCellAmount = 0;
            erasedCellAmount = 0;
            mouseChildren = true;
            mouseEnabled = true;
            for(var row:int=0; row<map.length; row++)
            {
                for(var col:int=0; col<map[row].length; col++)
                {
                    map[row][col].type = getNewType();
                    map[row][col].display();
                }
            }
            checkMap();
        }

        public function stop():void
        {
            mouseChildren = false;
            mouseEnabled = false;
        }

        public function checkMap():void
        {
            for(var row:int=0; row<map.length; row++)
            {
                for(var col:int=0; col<map[row].length; col++)
                {
                    while(isErasable(map[row][col]))
                    {
                        map[row][col].type = getNewType();
                        map[row][col].display();
                    }
                }
            }
        }

        public function getNewType():int
        {
            //return Cell.TYPE_AMOUNT * Math.random();
            return 4 * Math.random();
        }

        public function isErasable(a:Cell):Boolean
        {
            if(a == null) return false;
            for(var i:int=0; i<DIRECTION/2; i++)
            {
                var col:int = a.col;
                var row:int = a.row;
                var sum:int = 0;
                var cell:Cell = getCell(col, row);
                while((cell != null) && (cell.type == a.type))
                {
                    sum++;
                    col += DX[(row & 1)][i];
                    row += DY[(row & 1)][i];
                    if(sum >= ERASABLE_LIMIT) return true;
                    cell = getCell(col, row);
                }
                col = a.col;
                row = a.row;
                cell = getCell(col, row);
                sum --;
                while((cell != null) && (cell.type == a.type))
                {
                    sum++;
                    col += DX[(row & 1)][i + DIRECTION/2];
                    row += DY[(row & 1)][i + DIRECTION/2];
                    if(sum >= ERASABLE_LIMIT) return true;
                    cell = getCell(col, row);
                }
            }
            return false;
        }

        public function isAdjacent(a:Cell, b:Cell):Boolean
        {
            if((a == null) || (b == null)) return false;
            for(var i:int=0; i<DIRECTION; i++)
            {
                if(((a.col + DX[(a.row & 1)][i]) == b.col) &&
                   ((a.row + DY[(a.row & 1)][i]) == b.row))
                {
                    return true;
                }
            }
            return false;
        }

        public function isSwappable(a:Cell, b:Cell):Boolean
        {
            if((a == null) || (b == null)) return false;
            if(isAdjacent(a, b))
            {
                swapCellsType(a, b);
                if(isErasable(a))
                {
                    swapCellsType(a, b);
                    return true;
                }
                if(isErasable(b))
                {
                    swapCellsType(a, b);
                    return true;
                }
                swapCellsType(a, b);
            }
            return false;
        }

        public function eraseCell(a:Cell):int
        {
            var eraseList:Array = new Array();
            var sum:int = 0;
            if((a.type != Cell.TYPE_EMPTY) && isErasable(a))
            {
                eraseList.push(a);
                a.isErase = true;
            }
            var l:int = 0;
            while(l < eraseList.length)
            {
                for(var i:int=0; i<DIRECTION; i++)
                {
                    var col:int = eraseList[l].col;
                    var row:int = eraseList[l].row;
                    col += DX[(row & 1)][i];
                    row += DY[(row & 1)][i];
                    var cell:Cell = getCell(col, row);
                    if(isErasable(cell) && cell.isErase == false)
                    {
                        cell.isErase = true;
                        eraseList.push(cell);
                    }
                }
                l++;
            }
            if(eraseList.length >= ERASABLE_LIMIT)
            {
                sum = eraseList.length;
            }
            for(i=eraseList.length-1; i>=0; i--)
            {
                eraseList[i].erase();
                eraseList.pop();
            }
            return sum;
        }

        public function eraseCells():void
        {
            state = STATE_ERASING;
            var sum:int = 0;
            for(var row:int=0; row<map.length; row++)
            {
                for(var col:int=0; col<map[row].length; col++)
                {
                    sum += eraseCell(map[row][col]);
                }
            }
            trace("Event: eraseCells = " + sum.toString());
            if(sum > 0)
            {
                erasingCellAmount += sum;
            }
            else
            {
                mouseChildren = true;
                mouseEnabled = true;
                state = STATE_NORMAL;
            }
        }

        public function swapCells(a:Cell, b:Cell):void
        {
            mouseChildren = false;
            mouseEnabled = false;
            state = STATE_SWAPPING;
            movingCellAmount += 2;
            a.moveTo(b);
            b.moveTo(a);
        }

        public function swapCellsType(a:Cell, b:Cell):void
        {
            var temp:int = a.type;
            a.type = b.type;
            b.type = temp;
        }

        public function createCells():void
        {
            trace("Event: Create Cells");
            for(var i:int=0; i<COL; i++)
            {
                if(map[0][i].type == Cell.TYPE_EMPTY)
                {
                    map[0][i].type = getNewType();
                    map[0][i].display();
                    erasedCellAmount--;
                }
            }
            if(erasedCellAmount <= 0)
            {
                trace("Event: Create Cells to erase");
                eraseCells();
            }
            else
            {
                trace("Event: Create Cells to drop");
                dropCells();
            }
        }

        public function dropCells():void
        {
            trace("Event: Drop Cells");
            state = STATE_DROPPING;
            var sum:int = 0;
            for(var row:int=map.length-1; row>0; row--)
            {
                for(var col:int=0; col<map[row].length; col++)
                {
                    if(map[row][col].type == Cell.TYPE_EMPTY)
                    {
                        sum ++;
                        var c:int = map[row][col].col;
                        var r:int = map[row][col].row;
                        var cell:Cell = null;
                        var n:int = DIRECTION - 1;
                        if((row & 1) == 0)
                        {
                            c += DX[0][0];
                            r += DY[0][0];
                        }
                        else
                        {
                            c += DX[1][n];
                            r += DY[1][n];
                        }
                        cell = getCell(c, r);
                        if((cell != null) &&
                           (cell.type != Cell.TYPE_EMPTY) &&
                           (cell.isMoving == false))
                        {
                            movingCellAmount += 2;
                            cell.moveTo(map[row][col]);
                            map[row][col].moveTo(cell);
                            continue;
                        }

                        c = map[row][col].col;
                        r = map[row][col].row;
                        if((row & 1) == 0)
                        {
                            c += DX[0][n];
                            r += DY[0][n];
                        }
                        else
                        {
                            c += DX[1][0];
                            r += DY[1][0];
                        }
                        cell = getCell(c, r);
                        if((cell != null) &&
                           (cell.type != Cell.TYPE_EMPTY) &&
                           (cell.isMoving == false))
                        {
                            movingCellAmount += 2;
                            cell.moveTo(map[row][col]);
                            map[row][col].moveTo(cell);
                            continue;
                        }
                    }
                }
            }
        }

        public function getCell(col:int, row:int):Cell
        {
            if(row >= ROW || row < 0) return null;
            if((row&1) == 1)
            {
                if(col >= COL + 1 || col < 0) return null;
            }
            else
            {
                if(col >= COL || col < 0) return null;
            }
            return map[row][col];
        }

        public function mouseDown(e:Event):void
        {
            if(selectCell != null && selectCell == Cell(e.currentTarget))
            {
                selectCell.alpha = 0.5;
                selectCell = null;
            }
            else
            {
                if(selectCell != null)
                {
                    selectCell.alpha = 1;
                }
                selectCell = Cell(e.currentTarget);
                selectCell.alpha = 0.3;
            }
        }

        public function mouseOver(e:Event):void
        {
            if(selectCell == null || selectCell != Cell(e.currentTarget))
            {
                overCell = Cell(e.currentTarget);
                overCell.alpha = 0.5;
            }
            if(selectCell != null && selectCell != Cell(e.currentTarget))
            {
                overCell = Cell(e.currentTarget);
                if(isSwappable(selectCell, overCell))
                {
                    selectCell.alpha = 1;
                    overCell.alpha = 1;
                    swapCells(selectCell, overCell);
                    selectCell = null;
                    overCell = null;
                }
            }
        }

        public function mouseOut(e:Event):void
        {
            if(selectCell == null || selectCell != Cell(e.currentTarget))
            {
                e.currentTarget.alpha = 1;
            }
        }

        public function cellMoved(e:Event):void
        {
            trace("Event: Cell Moved");
            movingCellAmount--;
            if(movingCellAmount <= 0)
            {
                if(state == STATE_SWAPPING)
                {
                    eraseCells();
                }
                if(state == STATE_DROPPING)
                {
                    createCells();
                }
            }
        }

        public function cellErased(e:Event):void
        {
            trace("Event: Cell Erased");
            erasingCellAmount--;
            erasedCellAmount++;
            trace("Info: erasingCellAmount=" + erasingCellAmount.toString());
            if(erasingCellAmount <= 0)
            {
                if(state == STATE_ERASING)
                {
                    createCells();
                }
            }
        }
    }
}
